/*
 * Copyright 2008-2018 liandishop.net. All rights reserved.
 * Support: http://www.liandishop.net
 * License: http://www.liandishop.net/license
 * FileId: imnfiOPuYR38Bp0NpX9oobt8mcnux5XY/8oBZx6gwhI=
 */
package com.cygsunri.util;

import net.sf.json.JSONObject;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Utils - Web
 *
 * @author liandishop Team
 * @version 6.0
 */
public class WebUtils
{

	/**
	 * PoolingHttpClientConnectionManager
	 */
	private static final PoolingHttpClientConnectionManager HTTP_CLIENT_CONNECTION_MANAGER;

	/**
	 * CloseableHttpClient
	 */
	private static final CloseableHttpClient HTTP_CLIENT;

	static
	{
		HTTP_CLIENT_CONNECTION_MANAGER = new PoolingHttpClientConnectionManager(RegistryBuilder.<ConnectionSocketFactory> create().register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", SSLConnectionSocketFactory.getSocketFactory()).build());
		HTTP_CLIENT_CONNECTION_MANAGER.setDefaultMaxPerRoute(100);
		HTTP_CLIENT_CONNECTION_MANAGER.setMaxTotal(200);
		RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(60000).setConnectTimeout(60000).setSocketTimeout(60000).build();
		HTTP_CLIENT = HttpClientBuilder.create().setConnectionManager(HTTP_CLIENT_CONNECTION_MANAGER).setDefaultRequestConfig(requestConfig).build();
	}

	/**
	 * 不可实例化
	 */
	private WebUtils()
	{
	}

	/**
	 * 获取HttpServletRequest
	 *
	 * @return HttpServletRequest
	 */
	public static HttpServletRequest getRequest()
	{
		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
		return requestAttributes != null && requestAttributes instanceof ServletRequestAttributes ? ((ServletRequestAttributes) requestAttributes).getRequest() : null;
	}

	/**
	 * 获取HttpServletResponse
	 *
	 * @return HttpServletResponse
	 */
	public static HttpServletResponse getResponse()
	{
		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
		return requestAttributes != null && requestAttributes instanceof ServletRequestAttributes ? ((ServletRequestAttributes) requestAttributes).getResponse() : null;
	}


	/**
	 * 添加cookie
	 *
	 * @param request
	 *            HttpServletRequest
	 * @param response
	 *            HttpServletResponse
	 * @param name
	 *            Cookie名称
	 * @param value
	 *            Cookie值
	 * @param maxAge
	 *            有效期(单位: 秒)
	 * @param path
	 *            路径
	 * @param domain
	 *            域
	 * @param secure
	 *            是否启用加密
	 */
	public static void addCookie(HttpServletRequest request, HttpServletResponse response, String name, String value, Integer maxAge, String path, String domain, Boolean secure)
	{
		Assert.notNull(request, "[Assertion failed] - request is required; it must not be null");
		Assert.notNull(response, "[Assertion failed] - response is required; it must not be null");
		Assert.hasText(name, "[Assertion failed] - name must have text; it must not be null, empty, or blank");
		Assert.hasText(value, "[Assertion failed] - value must have text; it must not be null, empty, or blank");

		try
		{
			name = URLEncoder.encode(name, "UTF-8");
			value = URLEncoder.encode(value, "UTF-8");
			Cookie cookie = new Cookie(name, value);
			if (maxAge != null)
			{
				cookie.setMaxAge(maxAge);
			}
			if (StringUtils.isNotEmpty(path))
			{
				cookie.setPath(path);
			}
			if (StringUtils.isNotEmpty(domain))
			{
				cookie.setDomain(domain);
			}
			if (secure != null)
			{
				cookie.setSecure(secure);
			}
			response.addCookie(cookie);
		}
		catch (UnsupportedEncodingException e)
		{
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	/**
	 * 获取cookie
	 *
	 * @param request
	 *            HttpServletRequest
	 * @param name
	 *            Cookie名称
	 * @return Cookie值，若不存在则返回null
	 */
	public static String getCookie(HttpServletRequest request, String name)
	{
		Assert.notNull(request, "[Assertion failed] - request is required; it must not be null");
		Assert.hasText(name, "[Assertion failed] - name must have text; it must not be null, empty, or blank");

		Cookie[] cookies = request.getCookies();
		if (cookies != null)
		{
			try
			{
				name = URLEncoder.encode(name, "UTF-8");
				for (Cookie cookie : cookies)
				{
					if (StringUtils.equals(name, cookie.getName()))
					{
						return URLDecoder.decode(cookie.getValue(), "UTF-8");
					}
				}
			}
			catch (UnsupportedEncodingException e)
			{
				throw new RuntimeException(e.getMessage(), e);
			}
		}
		return null;
	}

	/**
	 * 移除cookie
	 *
	 * @param request
	 *            HttpServletRequest
	 * @param response
	 *            HttpServletResponse
	 * @param name
	 *            Cookie名称
	 * @param path
	 *            路径
	 * @param domain
	 *            域
	 */
	public static void removeCookie(HttpServletRequest request, HttpServletResponse response, String name, String path, String domain)
	{
		Assert.notNull(request, "[Assertion failed] - request is required; it must not be null");
		Assert.notNull(response, "[Assertion failed] - response is required; it must not be null");
		Assert.hasText(name, "[Assertion failed] - name must have text; it must not be null, empty, or blank");

		try
		{
			name = URLEncoder.encode(name, "UTF-8");
			Cookie cookie = new Cookie(name, null);
			cookie.setMaxAge(0);
			if (StringUtils.isNotEmpty(path))
			{
				cookie.setPath(path);
			}
			if (StringUtils.isNotEmpty(domain))
			{
				cookie.setDomain(domain);
			}
			response.addCookie(cookie);
		}
		catch (UnsupportedEncodingException e)
		{
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	/**
	 * 参数解析
	 *
	 * @param query
	 *            查询字符串
	 * @param encoding
	 *            编码格式
	 * @return 参数
	 */
	public static Map<String, String> parse(String query, String encoding)
	{
		Assert.hasText(query, "[Assertion failed] - query must have text; it must not be null, empty, or blank");

		Charset charset;
		if (StringUtils.isNotEmpty(encoding))
		{
			charset = Charset.forName(encoding);
		}
		else
		{
			charset = Charset.forName("UTF-8");
		}
		List<NameValuePair> nameValuePairs = URLEncodedUtils.parse(query, charset);
		Map<String, String> parameterMap = new HashMap<>();
		for (NameValuePair nameValuePair : nameValuePairs)
		{
			parameterMap.put(nameValuePair.getName(), nameValuePair.getValue());
		}
		return parameterMap;
	}

	/**
	 * 解析参数
	 *
	 * @param query
	 *            查询字符串
	 * @return 参数
	 */
	public static Map<String, String> parse(String query)
	{
		Assert.hasText(query, "[Assertion failed] - query must have text; it must not be null, empty, or blank");

		return parse(query, null);
	}

	/**
	 * 重定向
	 *
	 * @param request
	 *            HttpServletRequest
	 * @param response
	 *            HttpServletResponse
	 * @param url
	 *            URL
	 * @param contextRelative
	 *            是否相对上下文路径
	 * @param http10Compatible
	 *            是否兼容HTTP1.0
	 */
	public static void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url, boolean contextRelative, boolean http10Compatible)
	{
		Assert.notNull(request, "[Assertion failed] - request is required; it must not be null");
		Assert.notNull(response, "[Assertion failed] - response is required; it must not be null");
		Assert.hasText(url, "[Assertion failed] - url must have text; it must not be null, empty, or blank");

		StringBuilder targetUrl = new StringBuilder();
		if (contextRelative && url.startsWith("/"))
		{
			targetUrl.append(request.getContextPath());
		}
		targetUrl.append(url);
		String encodedRedirectURL = response.encodeRedirectURL(String.valueOf(targetUrl));
		if (http10Compatible)
		{
			try
			{
				response.sendRedirect(encodedRedirectURL);
			}
			catch (IOException e)
			{
				throw new RuntimeException(e.getMessage(), e);
			}
		}
		else
		{
			response.setStatus(303);
			response.setHeader("Location", encodedRedirectURL);
		}
	}

	/**
	 * 重定向
	 *
	 * @param request
	 *            HttpServletRequest
	 * @param response
	 *            HttpServletResponse
	 * @param url
	 *            URL
	 */
	public static void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
	{
		sendRedirect(request, response, url, true, true);
	}

	/**
	 * POST请求
	 *
	 * @param url
	 *            URL
	 * @param xml
	 *            XML
	 * @return 返回结果
	 */
	public static String post(String url, String xml)
	{
		Assert.hasText(url, "[Assertion failed] - url must have text; it must not be null, empty, or blank");

		return post(url, null, new StringEntity(xml, "UTF-8"));
	}

	/**
	 * POST请求
	 *
	 * @param url
	 *            URL
	 * @param parameterMap
	 *            请求参数
	 * @return 返回结果
	 */
	public static String post(String url, Map<String, Object> parameterMap)
	{
		Assert.hasText(url, "[Assertion failed] - url must have text; it must not be null, empty, or blank");

		try
		{
			List<NameValuePair> nameValuePairs = new ArrayList<>();
			if (parameterMap != null)
			{
				for (Map.Entry<String, Object> entry : parameterMap.entrySet())
				{
					String name = entry.getKey();
					String value = ConvertUtils.convert(entry.getValue());
					if (StringUtils.isNotEmpty(name))
					{
						nameValuePairs.add(new BasicNameValuePair(name, value));
					}
				}
			}
			return post(url, null, new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));
		}
		catch (UnsupportedEncodingException e)
		{
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	/**
	 * POST请求
	 *
	 * @param url
	 *            URL
	 * @param header
	 *            Header
	 * @param entity
	 *            HttpEntity
	 * @return 返回结果
	 */
	public static String post(String url, Header header, HttpEntity entity)
	{
		return post(url, header, entity, String.class);
	}

	/**
	 * POST请求
	 *
	 * @param url
	 *            URL
	 * @param header
	 *            Header
	 * @param entity
	 *            HttpEntity
	 * @param resultType
	 *            返回结果类型
	 * @return 返回结果
	 */
	@SuppressWarnings("unchecked")
	public static <T> T post(String url, Header header, HttpEntity entity, Class<T> resultType)
	{
		Assert.hasText(url, "[Assertion failed] - url must have text; it must not be null, empty, or blank");
		Assert.notNull(resultType, "[Assertion failed] - resultType is required; it must not be null");

		try
		{
			HttpPost httpPost = new HttpPost(url);
			if (header != null)
			{
				httpPost.setHeader(header);
			}
			if (entity != null)
			{
				httpPost.setEntity(entity);
			}
			CloseableHttpResponse httpResponse = HTTP_CLIENT.execute(httpPost);
			HttpEntity httpEntity = null;
			try
			{
				httpEntity = httpResponse.getEntity();
				if (httpEntity != null)
				{
					if (String.class.isAssignableFrom(resultType))
					{
						return (T) EntityUtils.toString(httpEntity, "UTF-8");
					}
					else if (resultType.isArray() && byte.class.isAssignableFrom(resultType.getComponentType()))
					{
						return (T) EntityUtils.toByteArray(httpEntity);
					}
				}
			}
			finally
			{
				EntityUtils.consume(httpEntity);
				IOUtils.closeQuietly(httpResponse);
			}
		}
		catch (UnsupportedEncodingException e)
		{
			throw new RuntimeException(e.getMessage(), e);
		}
		catch (ParseException e)
		{
			throw new RuntimeException(e.getMessage(), e);
		}
		catch (IOException e)
		{
			throw new RuntimeException(e.getMessage(), e);
		}
		return null;
	}

	/**
	 * GET请求
	 *
	 * @param url
	 *            URL
	 * @param parameterMap
	 *            请求参数
	 * @return 返回结果
	 */
	public static String get(String url, Map<String, Object> parameterMap)
	{
		Assert.hasText(url, "[Assertion failed] - url must have text; it must not be null, empty, or blank");

		String result = null;
		try
		{
			List<NameValuePair> nameValuePairs = new ArrayList<>();
			if (parameterMap != null)
			{
				for (Map.Entry<String, Object> entry : parameterMap.entrySet())
				{
					String name = entry.getKey();
					String value = ConvertUtils.convert(entry.getValue());
					if (StringUtils.isNotEmpty(name))
					{
						nameValuePairs.add(new BasicNameValuePair(name, value));
					}
				}
			}
			HttpGet httpGet = new HttpGet(url + (StringUtils.contains(url, "?") ? "&" : "?") + EntityUtils.toString(new UrlEncodedFormEntity(nameValuePairs, "UTF-8")));
			CloseableHttpResponse httpResponse = HTTP_CLIENT.execute(httpGet);
			try
			{
				HttpEntity httpEntity = httpResponse.getEntity();
				if (httpEntity != null)
				{
					result = EntityUtils.toString(httpEntity);
					EntityUtils.consume(httpEntity);
				}
			}
			finally
			{
				IOUtils.closeQuietly(httpResponse);
			}
		}
		catch (UnsupportedEncodingException e)
		{
			throw new RuntimeException(e.getMessage(), e);
		}
		catch (ParseException e)
		{
			throw new RuntimeException(e.getMessage(), e);
		}
		catch (ClientProtocolException e)
		{
			throw new RuntimeException(e.getMessage(), e);
		}
		catch (IOException e)
		{
			throw new RuntimeException(e.getMessage(), e);
		}
		return result;
	}

	/**
	 * post请求
	 * @param url
	 * @param json
	 * @return
	 */

	public static JSONObject doPost(String url, JSONObject json)
	{
		DefaultHttpClient client = new DefaultHttpClient();
		HttpPost post = new HttpPost(url);
		post.addHeader("Content-type", "application/json; charset=utf-8");
		post.setHeader("Accept", "application/json");
		JSONObject response = null;
		try
		{
			StringEntity s = new StringEntity(json.toString(), Charset.forName("UTF-8"));
			s.setContentEncoding("UTF-8");
			post.setEntity(s);
			HttpResponse res = client.execute(post);
			if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
			{
				HttpEntity entity = res.getEntity();
				String result = EntityUtils.toString(res.getEntity());// 返回json格式：
				response = JSONObject.fromObject(result);
			}
		}
		catch (Exception e)
		{
			throw new RuntimeException(e);
		}
		return response;
	}

	/**
	 * 向微信公众号上传图片
	 * @param path
	 * @param file
	 * @return
	 * @throws IOException
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchProviderException
	 * @throws KeyManagementException
	 */
	public static String connectHttpsByPost(String path, File file) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException
	{
		URL urlObj = new URL(path);
		//连接
		HttpURLConnection con = (HttpURLConnection) urlObj.openConnection();
		String result = null;
		con.setDoInput(true);

		con.setDoOutput(true);

		con.setUseCaches(false); // post方式不能使用缓存

		// 设置请求头信息
		con.setRequestProperty("Connection", "Keep-Alive");
		con.setRequestProperty("Charset", "UTF-8");
		// 设置边界
		String BOUNDARY = "----------" + System.currentTimeMillis();
		con.setRequestProperty("Content-Type",
				"multipart/form-data; boundary="
						+ BOUNDARY);

		// 请求正文信息
		// 第一部分：
		StringBuilder sb = new StringBuilder();
		sb.append("--"); // 必须多两道线
		sb.append(BOUNDARY);
		sb.append("\r\n");
		sb.append("Content-Disposition: form-data;name=\"buffer\";filelength=\"" + file.length() + "\";filename=\""

				+ file.getName() + "\"\r\n");
		sb.append("Content-Type:application/octet-stream\r\n\r\n");
		byte[] head = sb.toString().getBytes("utf-8");
		// 获得输出流
		OutputStream out = new DataOutputStream(con.getOutputStream());
		// 输出表头
		out.write(head);

		// 文件正文部分
		// 把文件已流文件的方式 推入到url中
		DataInputStream in = new DataInputStream(new FileInputStream(file));
		int bytes = 0;
		byte[] bufferOut = new byte[1024];
		while ((bytes = in.read(bufferOut)) != -1)
		{
			out.write(bufferOut, 0, bytes);
		}
		in.close();
		// 结尾部分
		byte[] foot = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("utf-8");// 定义最后数据分隔线
		out.write(foot);
		out.flush();
		out.close();
		StringBuffer buffer = new StringBuffer();
		BufferedReader reader = null;
		try
		{
			// 定义BufferedReader输入流来读取URL的响应
			reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
			String line = null;
			while ((line = reader.readLine()) != null)
			{
				buffer.append(line);
			}
			if (result == null)
			{
				result = buffer.toString();
			}
		}
		catch (IOException e)
		{
			//System.out.println("发送POST请求出现异常！" + e);
			e.printStackTrace();
		}
		finally
		{
			if (reader != null)
			{
				reader.close();
			}
		}
		return result;
	}

}